package org.javaboy.jwt_redis_demo.config;

import io.jsonwebtoken.Claims;
import io.jsonwebtoken.Jwts;
import org.springframework.core.io.ClassPathResource;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.util.FileCopyUtils;

import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.security.KeyFactory;
import java.security.NoSuchAlgorithmException;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.security.interfaces.RSAPublicKey;
import java.security.spec.InvalidKeySpecException;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.X509EncodedKeySpec;
import java.util.Arrays;
import java.util.Base64;
import java.util.Date;
import java.util.List;
import java.util.stream.Collectors;

public class JwtUtils {

    public static final Integer EXPIRE_TIME = 7 * 24 * 60 * 60;
    public static final Integer REDIS_TOKEN_EXPIRE_TIME = 30 * 60;
    public static final String TOKEN_ISSUER = "javaboy";

    /**
     * 生成 JWT 字符串
     *
     * @return
     */
    public static String createJWT() throws IOException, NoSuchAlgorithmException, InvalidKeySpecException {
        Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
        String jws = Jwts.builder()
                //设置头部信息(相当于 JWT 字符串第一部分的内容)
                .header().add("type", "JWT")
                .and()
                //相当于 JWT 字符串第二部分的内容
                //签名密钥
                .signWith(getPrivateKey())
                //设置过期时间
                .expiration(java.util.Date.from(java.time.Instant.now().plusSeconds(EXPIRE_TIME)))
                //设置用户名
                .subject(authentication.getName())
                //设置用户权限，相当于把用户权限转为字符串 admin,user,xxx
                .claim("authorities", authentication.getAuthorities().stream().map(a -> a.getAuthority()).collect(Collectors.joining(",")))
                //令牌签发时间
                .issuedAt(new Date())
                .issuer(TOKEN_ISSUER)
                .compact();
        return jws;
    }

    public static PrivateKey getPrivateKey() throws IOException, NoSuchAlgorithmException, InvalidKeySpecException {
        ClassPathResource resource = new ClassPathResource("private_key.pem");
        byte[] privateKeyBytes = FileCopyUtils.copyToByteArray(resource.getInputStream());
        String key = new String(privateKeyBytes, StandardCharsets.UTF_8);
        String privateStr = key
                .replace("-----BEGIN PRIVATE KEY-----", "")
                .replaceAll(System.lineSeparator(), "")
                .replace("-----END PRIVATE KEY-----", "");
        byte[] privateKeyDecodedBytes = Base64.getDecoder().decode(privateStr);
        PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(privateKeyDecodedBytes);
        KeyFactory keyFactory = KeyFactory.getInstance("RSA");
        PrivateKey privateKey = keyFactory.generatePrivate(keySpec);
        return privateKey;
    }

    public static RSAPublicKey getPublicKey() throws IOException, NoSuchAlgorithmException, InvalidKeySpecException {
        ClassPathResource resource = new ClassPathResource("public_key.pem");
        byte[] publicKeyBytes = FileCopyUtils.copyToByteArray(resource.getInputStream());
        String key = new String(publicKeyBytes, StandardCharsets.UTF_8);
        String publicStr = key
                .replace("-----BEGIN PUBLIC KEY-----", "")
                .replaceAll(System.lineSeparator(), "")
                .replace("-----END PUBLIC KEY-----", "");
        KeyFactory keyFactory = KeyFactory.getInstance("RSA");
        byte[] publicKeyBase64Bytes = Base64.getDecoder().decode(publicStr);
        X509EncodedKeySpec publicKeySpec = new X509EncodedKeySpec(publicKeyBase64Bytes);
        PublicKey rsaPublicKey = keyFactory.generatePublic(publicKeySpec);
        return (RSAPublicKey) rsaPublicKey;
    }

    public static Authentication verifyToken(String token) {
        try {
            Claims claims = Jwts.parser()
                    .verifyWith(getPublicKey()).build()
                    .parseSignedClaims(token).getPayload();
            String username = claims.getSubject();
            // user,admin,xxx
            String authorities = claims.get("authorities", String.class);
            // 解析用户权限
            List<SimpleGrantedAuthority> list = Arrays.stream(authorities.split(",")).map(s -> new SimpleGrantedAuthority(s)).collect(Collectors.toList());
            return UsernamePasswordAuthenticationToken.authenticated(username, null, list);
        } catch (Exception e) {
            //令牌校验失败
            return null;
        }

    }
}
